module IPFS.Client.Utils.PrepareFile (
  prepareFile,
  PrepareFileOptions(..),
  defPrepareFileOptions
) where

import Prelude

import Data.Array (concat)
import Data.Maybe (fromJust)
import Data.Traversable (traverse)
import Effect (Effect)
import Effect.Exception (throw)
import Foreign (Foreign)
import Foreign.Object as O
import IPFS.Client.Utils.Types (LocalFile(..), SendFile, SendFileContent(..))
import Node.FS.Stats as F
import Node.FS.Stream as FS
import Node.FS.Sync as FSSync
import Node.Path as Path
import Node.StreamEx.Stream (fromReadableStream)
import Node.StreamEx.Types (Readable(..))
import Partial.Unsafe (unsafePartial)
import Unsafe.Coerce (unsafeCoerce)

type PrepareFileOptions = {
  followSymlinks::Boolean,
  recursive::Boolean,
  hidden::Boolean,
  ignore::Boolean
}
defPrepareFileOptions::PrepareFileOptions
defPrepareFileOptions =  {
  followSymlinks:false,
  recursive:true,
  hidden:false,
  ignore:false
}

foreign import listFiles::String -> Boolean -> Boolean -> Boolean -> Effect (Array (O.Object Foreign))

prepareFile::Array LocalFile -> PrepareFileOptions -> Effect (Array SendFile)
prepareFile localFiles options = concat <$> traverse toSendFile localFiles
　where
   toSendFile::LocalFile -> Effect (Array SendFile)
   toSendFile (LocalFileBuffer buf)            = pure [{ header:O.empty,　path:"",　symlink:false,　dir:false,　content:SendBuffer buf　}]
   toSendFile (LocalFilePath   filepath)       = loadPaths filepath options.followSymlinks options.recursive options.hidden options.ignore
   toSendFile (LocalFileReadStream readStream) = pure [{ header:O.empty,　path:"",　symlink:false,　dir:false,　content:SendReadable readStream　}]

loadPaths::String -> Boolean -> Boolean -> Boolean -> Boolean -> Effect (Array SendFile)
loadPaths localPath followSymlinks recursive hidden ignore = do
  resolvePath <- Path.resolve [] localPath
  stat <- FSSync.stat resolvePath
  loadPathIF stat resolvePath
 where
  loadPathIF stat resolvePath | F.isDirectory stat && (not recursive) = throw "Can only add directories using --recursive"
    　　　　　　　　　　　　　　　 | F.isDirectory stat && recursive = do
                                                                   arr <- listFiles resolvePath followSymlinks hidden ignore
                                                                   pure $ map sendFileFromJsObject arr
                              | otherwise = do
                                  readStream <- FS.createReadStream resolvePath
                                  pure [{ header:O.empty,
                                    　　　path:Path.basename resolvePath,
                                         content:SendReadable $ fromReadableStream readStream,
                                         symlink:false,dir:false
                                       }]

sendFileFromJsObject::O.Object Foreign -> SendFile
sendFileFromJsObject obj = { header:O.empty,
                             path:unsafeCoerce $ unsafeLookUp "path" obj,
                             symlink:isSymlink, 
                             dir:isDir,
                             content:toContext}
 where
   isSymlink = unsafeCoerce $ unsafeLookUp "symlink" obj
   isDir     = unsafeCoerce $ unsafeLookUp "dir" obj
   context::Foreign
   context   = unsafeLookUp "content" obj
   toContext |  isDir     = SendDir
             |  isSymlink = SendSymlink  (unsafeCoerce context)
             |  otherwise = SendReadable (Readable $ unsafeCoerce context)
   unsafeLookUp::String -> O.Object Foreign -> Foreign
   unsafeLookUp k o = unsafePartial $ fromJust $ O.lookup k o
   


